#include "knxengine.h"
#include "frame_interface.h"
#include "cmei.h"

#define TUNNELLING_CONTL_ONE 0x8C
#define TUNNELLING_CONTL_TWO 0xE0

void KNXEngine::tunneling_sync_start()
{
    g_tunnelling_sync_wait = false;
}

bool KNXEngine::tunneling_sync_wait()
{
    int count = 4;
    while(count -- >0 && g_tunnelling_sync_wait == false)
    {
        processEvents(50);
    }

    return g_tunnelling_sync_wait;
}

void KNXEngine::tunneling_sync_finish()
{
    g_tunnelling_sync_wait = true;
}

void KNXEngine::tunneling_ack_from_knx(_u8 *recv_buf, int msg_len)
{
    if(msg_len<10)
        return;

    _u8 cid = recv_buf[7];
    _u8 sc = recv_buf[8];
    _u8 errcode = recv_buf[9];

    //KDEBUG << "tunneling_ack_from_knx: cid =" << cid << "sc = " << sc <<" errcode =" << errcode;

    if (errcode != E_NO_ERROR) {
         KDEBUG << "!tunneling_ack_from_knx: error recv " << errcode;
    }

    if (sc != g_sequence_num) {

        if(sc<g_sequence_num)
        {
            return ;
        }
        else
        {
            //this ack is not the one we are waiting for....
            KDEBUG<< "!tunneling_ack_from_knx: sequence_num is not match the ack , use = " << g_sequence_num << "got = " << sc;

            return ;
        }
    }

    tunneling_sync_finish();
    sequence_num_add();

    if (cid != get_cid()) {
        KDEBUG << "!tunneling_ack_from_knx: cid is not match the ack " << cid << "!=" << get_cid();
    }

}


void KNXEngine::reset_tunnelling_sc()
{
    g_sequence_num = 0x0;
}

_u8 KNXEngine::sequence_num_add()
{
    g_sequence_num++;
    return g_sequence_num;
}

_u8 KNXEngine::get_sequence_num()
{
   return g_sequence_num;
}

void KNXEngine::set_sequence_num(_u8 sc)
{
    g_sequence_num = sc;
}

_u16 KNXEngine::onebit_tunnelling_request(_u8 channelID,_u8 writebuffer[],_u32 len, _u8 addr_array[2],_u8 value)
{
    _u16 total_len = 0;

    _u8 cntol1 = TUNNELLING_CONTL_ONE;
    _u8 cntol2 = TUNNELLING_CONTL_TWO;
    _u8 sc;

    sc = get_sequence_num(); //sequence_num_add();

    memset(writebuffer,0,len);

    frame_set_header(writebuffer+total_len,&total_len);
    frame_set_cmd(writebuffer+total_len,TUNNEL_REQUEST,&total_len);
    frame_padding_total_size(writebuffer+total_len,&total_len);
    frame_set_tunnelling_struct(writebuffer+total_len,channelID,sc,&total_len);

    package_cmei_msg(writebuffer+total_len,L_Data_req,0x0,cntol1,cntol2,addr_array,&total_len);

    package_cmei_msg_data(writebuffer+total_len,0x00,&value,1,&total_len);

    fram_set_total_len(writebuffer,total_len);

    //KDEBUG << "onebit_tunnelling_request: cid , sc is " << channelID << sc;

    return total_len;
}

_u16 KNXEngine::bytes_tunnelling_request(_u8 channelID,_u8 writebuffer[],_u32 len,_u8 addr_array[2],_u8 bytes[],_u8 bytes_len)
{

    _u16 total_len = 0;

    _u8 cntol1 = TUNNELLING_CONTL_ONE;
    _u8 cntol2 = TUNNELLING_CONTL_TWO;

    memset(writebuffer,0,len);

    frame_set_header(writebuffer+total_len,&total_len);
    frame_set_cmd(writebuffer+total_len,TUNNEL_REQUEST,&total_len);
    frame_padding_total_size(writebuffer+total_len,&total_len);
    frame_set_tunnelling_struct(writebuffer+total_len,channelID,get_sequence_num(),&total_len);

    package_cmei_msg(writebuffer+total_len,L_Data_req,0x0,cntol1,cntol2,addr_array,&total_len);

    package_cmei_msg_data(writebuffer+total_len,0x00,bytes,bytes_len,&total_len);

    fram_set_total_len(writebuffer,total_len);

    //KDEBUG << "bytes_tunnelling_request: cid , sc is " << channelID << get_sequence_num();

    return total_len;

}

_u16 KNXEngine::send_tunneling_ack(_u8 sc)
{
    _u16 total_len = 0;
    _u8 writebuffer[32] = {0};

    frame_set_header(writebuffer+total_len,&total_len);
    frame_set_cmd(writebuffer+total_len,TUNNEL_ACK,&total_len);
    frame_padding_total_size(writebuffer+total_len,&total_len);
    frame_set_tunnelling_struct(writebuffer+total_len,get_cid(),sc,&total_len);

    fram_set_total_len(writebuffer,total_len);

    //KDEBUG << "send_tunneling_ack: cid , sc is " << get_cid() << sc;

    udpSendToKnx((char *)writebuffer,total_len);

    return total_len;
}

_u8 * KNXEngine::tunneling_request_from_knx(_u8 *recv_buf, _u32 len)
{
    if(len<11)
    {
        KDEBUG << "tunneling_request_from_knx: too short len "<< len ;
        return NULL;
    }

    _u8 cid = recv_buf[7];
    _u8 sc = recv_buf[8];
    _u8 *cemi = &recv_buf[10];

    _u8 ctl1 = cmei_get_control1(cemi);

    //KDEBUG << "tunneling_request_from_knx: control1 , cid , sc = " << ctl1 << cid << sc;

    send_tunneling_ack(sc);

    return cemi;
}



////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
    routing functions
*/

#define COMMON_CONTL_ONE    0xBC
#define COMMON_CONTL_TWO    0xD0

_int32 KNXEngine::onebit_routing(_u8 writebuffer[],_u32 len, _u8 addr_array[2],_u8 value)
{
    _u16 total_len = 0;

    _u8 cntol1 = COMMON_CONTL_ONE;
    _u8 cntol2 = COMMON_CONTL_TWO;

    memset(writebuffer,0,len);

    frame_set_header(writebuffer+total_len,&total_len);
    frame_set_cmd(writebuffer+total_len,ROUTING_INDICATION,&total_len);
    frame_padding_total_size(writebuffer+total_len,&total_len);

//    writebuffer[0] = 0x06;      // Header size: 6 bytes
//    writebuffer[1] = 0x10;      // EIBnet IP Version
//    writebuffer[2] = 0x05;      // Tunneling Request (2 bytes)
//    writebuffer[3] = 0x30;      //  -- '' --
//
//    writebuffer[4] = 0x00;      // size (2 bytes)
//    writebuffer[5] = 0x11;      //  -- '' --

    package_cmei_msg(writebuffer+total_len,L_Data_ind,0x0,cntol1,cntol2,addr_array,&total_len);

    // cEMI frame ----------------------------------------------

//    int controlField1 = 0xba;      //not repeat, low
//    int controlField2 = 0xd0;      //group address
//
//    writebuffer[6] = 0x29;     // message code
//    writebuffer[7] = 0x00;     // additional info length : 0
//    writebuffer[8] = (byte)controlField1;
//    writebuffer[9] = (byte)controlField2;
//    writebuffer[10] = 0x00;     // source address hi byte  (to be filled by gateway)
//    writebuffer[11] = 0x00;     // source address lo byte  (to be filled by gateway)
//
//    writebuffer[12] = 0x0b;     // dest address hi byte
//    writebuffer[13] = 0x01;     // dest address lo byte   !!!!!!

    package_cmei_msg_data(writebuffer+total_len,0x00,&value,1,&total_len);

    fram_set_total_len(writebuffer,total_len);
//    writebuffer[14] = 0x01;     // data len
//    writebuffer[15] = 0x00;     // TPCI/APCI
//
//    writebuffer[16] = value;     // APCI/data

    /*
        06 10 05 30 00 10 | 29 00 |B0 50 |11 05| 77 01 00 80
     */

    return total_len;
}

_int32 KNXEngine::onebyte_routing(_u8 writebuffer[],_u32 len,_u8 addr_array[2],_u8 value)
{
    _u16 total_len = 0;

    _u8 cntol1 = COMMON_CONTL_ONE;
    _u8 cntol2 = COMMON_CONTL_TWO;

    _u8 data[2] ;
    data[0] = (byte)0x80;     // APCI/data
    data[1] = (byte)value;     // APCI/data

    memset(writebuffer,0,len);

    frame_set_header(writebuffer+total_len,&total_len);
    frame_set_cmd(writebuffer+total_len,ROUTING_INDICATION,&total_len);
    frame_padding_total_size(writebuffer+total_len,&total_len);

    package_cmei_msg(writebuffer+total_len,L_Data_ind,0x0,cntol1,cntol2,addr_array,&total_len);

    package_cmei_msg_data(writebuffer+total_len,0x00,data,0x02,&total_len);

    fram_set_total_len(writebuffer,total_len);
    /*
     06 10 05 30 00 10 | 29 00 B0 50 11 05 77 01 00 80
     06 10 05 30 00 10 | 29 00 B0 50 11 05 77 06 00 80
     06 10 05 30 00 10 | 29 00 B0 50 11 05 77 0D 00 80
     06 10 05 30 00 10 | 29 00 B0 50 11 05 77 0F 00 80
     */

    return total_len;
}

_int32 KNXEngine::bytes_routing(_u8 writebuffer[],_u32 len,_u8 addr_array[2],_u8 bytes[],_u8 bytes_len)
{
    _u16 total_len = 0;

    memset(writebuffer,0,len);

    _u8 cntol1 = COMMON_CONTL_ONE;
    _u8 cntol2 = COMMON_CONTL_TWO;

    frame_set_header(writebuffer+total_len,&total_len);
    frame_set_cmd(writebuffer+total_len,ROUTING_INDICATION,&total_len);
    frame_padding_total_size(writebuffer+total_len,&total_len);

    package_cmei_msg(writebuffer+total_len,L_Data_ind,0x0,cntol1,cntol2,addr_array,&total_len);

    package_cmei_msg_data(writebuffer+total_len,0x00,bytes,bytes_len,&total_len);

    fram_set_total_len(writebuffer,total_len);
    /*
     06 10 05 30 00 10 | 29 00 B0 50 11 05 77 01 00 80
     06 10 05 30 00 10 | 29 00 B0 50 11 05 77 06 00 80
     06 10 05 30 00 10 | 29 00 B0 50 11 05 77 0D 00 80
     06 10 05 30 00 10 | 29 00 B0 50 11 05 77 0F 00 80
     */

    return total_len;
}

_int32 KNXEngine::routing_indication_recv_analyze(_u8 data[],_u32 data_type,_u8 *operation_type,
                                       _u8 **ret_value,_u8 *ret_len)
{
    _u8 len = data[0];     // data len
    //_u8 tpci = data[1];      // TPCI/APCI
    _u8 *value = &data[2];
    *ret_value = value;
    *ret_len = len;

    if ( (*value&APCI_VALUE_RESP) == APCI_VALUE_RESP) {
        *operation_type = APCI_VALUE_RESP;
    }
    else if ((*value&APCI_VALUE_WRITE) == APCI_VALUE_WRITE)
    {
        *operation_type = APCI_VALUE_WRITE;
    }
    else
    {
        *operation_type = APCI_VALUE_READ;
    }

    switch (data_type) {
        case ONE_BIT_VALUE:
        case ONE_BYTE_VALUE:
        {
            if(len!=0x02)
            {
               // DEBUG("routing_indication_recv_analyze:
                //      data type and recv len is not match %d should be 2 \n",len);
            }
            break;
        }
        case TWO_BYTES_VALUE:
        {
            break;
        }

        default:
            break;
    }

    return SUCCESS;
}

/*
_int32 KNXEngine::routing_testing(_u8 addr[2],_u8 addr_type)
{
    _u8 writebuffer[32] = {0};

    writebuffer[0] = 0x06;      // Header size: 6 bytes
    writebuffer[1] = 0x10;      // EIBnet IP Version
    writebuffer[2] = 0x05;      // routing Request (2 bytes)
    writebuffer[3] = 0x30;      //  -- '' --
    //
    writebuffer[4] = 0x00;      // size (2 bytes)
    writebuffer[5] = 0x10;      //  -- '' --

    int controlField1 = 0xba;      //not repeat, low
    int controlField2 = 0xd0;      //group address
    //
    writebuffer[6] = 0x29;     // message code
    writebuffer[7] = 0x00;     // additional info length : 0
    writebuffer[8] = (byte)controlField1;
    writebuffer[9] = (byte)controlField2;
    writebuffer[10] = 0x00;     // source address hi byte  (to be filled by gateway)
    writebuffer[11] = 0x00;     // source address lo byte  (to be filled by gateway)
    //
    writebuffer[12] = addr[0];     // dest address hi byte
    writebuffer[13] = addr[1];     // dest address lo byte   !!!!!!
    writebuffer[14] = 0x01;     // data len
    writebuffer[15] = 0x00;     // TPCI/APCI
    //
    writebuffer[16] = 0x81;     // APCI/data

    //udp send
    //close(sockfd);

    return 0;
}
*/
